package com.lhl.jwt.util;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.lhl.jwt.domain.dto.PayloadDTO;
import com.lhl.jwt.exception.JwtExpiredException;
import com.lhl.jwt.exception.JwtSignatureVerifyException;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;

import java.text.ParseException;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @className: com.lhl.jwt.util.JwtUtil
 * @description: Token工具类
 * @author: king
 * @date: 2021-01-05 14:49
 **/
@Slf4j
public class JwtUtil {
    public static final String  TOKEN_SECRET          = "king";
    // 过期时间2个小时
    private static final long	EXPIRATION			= 120L;

    public static String createToken(String username, Collection<? extends GrantedAuthority> authorities, String salt) throws JOSEException {
        List<String> as = authorities.stream()
                .map((auth) -> auth.getAuthority())
                .collect(Collectors.toList());
        PayloadDTO payloadDTO = getDefaultPayloadDTO();
        payloadDTO.setSub(username);
        payloadDTO.setUsername(username);
        payloadDTO.setAuthorities(as);
        // 生成token
        String payloadJsonString = JSONUtil.toJsonStr(payloadDTO);

        return createToken(payloadJsonString,StrUtil.isNotBlank(salt)?salt: SecureUtil.md5(TOKEN_SECRET));
    }

    public static String createToken(String payloadJsonString, String secret) throws JOSEException {
        // 创建Header(设置以JWSAlgorithm.HS256算法进行签名认证)
        JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
        // 建立Payload
        Payload payload = new Payload(payloadJsonString);

        /// Signature相关
        // 根据Header以及Payload创建JSON Web Signature (JWS)对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        // 使用给的对称加密密钥，创建一个加密器
        JWSSigner jwsSigner = new MACSigner(StrUtil.isNotBlank(secret)?secret:SecureUtil.md5(TOKEN_SECRET));
        // 将该签名器 与 JSON Web Signature (JWS)对象进行关联
        // 即: 指定JWS使用该签名器进行签名
        jwsObject.sign(jwsSigner);

        // 使用JWS生成JWT(即:使用JWS生成token)
        return jwsObject.serialize();
    }
    public static PayloadDTO getPayLoadByToken(String token, String secret) throws ParseException, JOSEException {
        // 解析token，将token转换为JWSObject对象
        JWSObject jwsObject = JWSObject.parse(token);

        // 创建一个JSON Web Signature (JWS) verifier.用于校验签名(即:校验token是否被篡改)
        JWSVerifier jwsVerifier = new MACVerifier(StrUtil.isNotBlank(secret)?secret:SecureUtil.md5(TOKEN_SECRET));
        // 如果校验到token被篡改(即:签名认证失败)，那么抛出异常
        if(!jwsObject.verify(jwsVerifier)) {
            throw new JwtSignatureVerifyException("签名认证失败!");
        }
        String payload = jwsObject.getPayload().toString();
        PayloadDTO payloadDto = JSONUtil.toBean(payload, PayloadDTO.class);
        if (payloadDto.getExp()<Instant.now().getEpochSecond()) {
            throw new JwtExpiredException("token已过期！");
        }
        return payloadDto;
    }

    /**
     * @param: sub
     * @description: 获得初始PayloadDTO数据
     * @return: com.lhl.jwt.domain.dto.PayloadDTO
     * @author: king
     * @date: 2021-01-11 10:50
     */
    public static PayloadDTO getDefaultPayloadDTO(){
        Instant now = Instant.now();
        Instant exp = now.plusSeconds(EXPIRATION);
        log.debug("now=={}", LocalDateTimeUtil.of(now));
        log.debug("now=={}", now.toEpochMilli());
        return PayloadDTO.builder()
                .iat(now.getEpochSecond())
                .exp(exp.getEpochSecond())
                .jti(UUID.randomUUID().toString())
                .build();

    }
}
